//#include "BigIntBasicTest.h"
//#include "BigInteger2.h"

#include "BigIntBasicTest.h"
#include "BigInteger.h"
#include <iostream>
#include <cassert>

typedef BigInteger BigInt;
namespace big
{
	BigInt Fibonacci(unsigned int n)
	{
		if (n == 0) 
			return 0;
		if (n == 1)
			return 1;

		BigInt fn1 = 0;
		BigInt fn2 = 1;

		BigInt fn;

		for (unsigned int i = 1; i < n; ++i)
		{
			fn	= fn1 + fn2;
			fn2 = fn1;
			fn1 = fn;
		}

		return fn;
	}

	BigInt Factorial(unsigned int n)
	{
		if (n == 0)
			return 1;

		BigInt fn = n;

		for (unsigned int i = 2; i < n; ++i)
		{
			fn *= i;
		}

		return fn;
	}

	void StringTest()
	{
		std::cout << "********************************* StringTest() *********************************" << std::endl << std::endl;

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;

		std::cout << "Get a BigInt (b1) from a string...";
		BigInt b1 = "123456789012345678901234567890";
		std::cout << "OK." << std::endl;

		std::cout << "b1 = " << b1 << std::endl;

		assert(b1.IsEven());

		std::cout << "Get a BigInt (b2) from a string...";
		BigInt b2 = "123456789012345678901234567890";
		std::cout << "OK." << std::endl;

		std::cout << "b2 = " << b2 << std::endl;

		assert(b2.IsEven());

		BigInt ratio = b1 / b2;

		std::cout << "Let's check this relationship: b1 / b2 = 1..."; 
		assert(ratio == 1);
		std::cout << "OK." << std::endl;

//		assert(ratio.IsOdd());

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;	
	}

	void FibonacciTest() 
	{
		std::cout << "******************************* FibonacciTest() ********************************" << std::endl << std::endl;

		const unsigned int N = 200;

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;

		BigInt fib;

		for (unsigned int n = 0; n <= N; ++n)
		{
			fib = Fibonacci(n);
			std::cout << "F(" << n << ") = " << fib << std::endl;
		}

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;

		BigInt fibn		= Fibonacci(N);
		BigInt fibn1	= Fibonacci(N - 1);
		BigInt fibn2	= Fibonacci(N - 2);

		std::cout << "F(" << N		<< ") = " << fibn	<< std::endl;
		std::cout << "F(" << N - 1	<< ") = " << fibn1	<< std::endl;
		std::cout << "F(" << N - 2	<< ") = " << fibn2	<< std::endl;

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;

		std::cout << "Let's check this relationship: F(" << N << ") - F(" << N - 2 << ") = F(" << N - 1 << ")" << std::endl; 

		BigInt diff1 = fibn - fibn2;

		std::cout << "F(" << N << ") - F(" << N - 2 << ") = " << diff1 << std::endl;
		assert(diff1 == fibn1);

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;

		std::cout << "Let's check this relationship: F(" << N - 2 << ") - F(" << N << ") = - F(" << N - 1 << ")" << std::endl;
		
		BigInt diff2 = fibn2 - fibn;

		std::cout << "F(" << N - 2 << ") - F(" << N << ") = " << diff2 << std::endl;
		assert(diff2 == -fibn1);

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;

		std::cout << "Let's check this relationship: F(" << N - 2 << ") < F(" << N << ").....";	
		assert(fibn2 < fibn);
		std::cout << "OK." << std::endl;

		std::cout << "Let's check this relationship: F(" << N - 2 << ") < F(" << N - 1 << ").....";	
		assert(fibn2 < fibn1);
		std::cout << "OK." << std::endl;

		std::cout << "Let's check this relationship: F(" << N << ") > F(" << N - 2 << ").....";	
		assert(fibn > fibn2);
		std::cout << "OK." << std::endl;

		std::cout << "Let's check this relationship: F(" << N << ") > F(" << N - 1 << ").....";	
		assert(fibn > fibn1);
		std::cout << "OK." << std::endl;

		std::cout << "Let's check this relationship: -F(" << N << ") < F(" << N - 2 << ")....";	
		assert(-fibn < fibn2);
		std::cout << "OK." << std::endl;

		std::cout << "Let's check this relationship: -F(" << N << ") < F(" << N - 1 << ")....";	
		assert(-fibn < fibn1);
		std::cout << "OK." << std::endl;

		std::cout << "Let's check this relationship: -F(" << N - 2 << ") < F(" << N << ")....";	
		assert(-fibn2 < fibn);
		std::cout << "OK." << std::endl;

		std::cout << "Let's check this relationship: -F(" << N - 2 << ") > -F(" << N << ")...";	
		assert(-fibn2 > -fibn);
		std::cout << "OK." << std::endl;

		std::cout << "Let's check this relationship: -F(" << N - 1 << ") > -F(" << N << ")...";	
		assert(-fibn1 > -fibn);
		std::cout << "OK." << std::endl;

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;

		BigInt prod1	= fibn	* fibn;
		BigInt prod2	= fibn1 * fibn;
		BigInt prod3	= fibn2 * fibn;

		BigInt prod23	= prod2 + prod3;
			
		BigInt err		= prod1 - prod23;

		std::cout << "P1 = F(" << N << ") * F(" << N << ") = "		<< prod1 << std::endl;
		std::cout << "P2 = F(" << N - 1 << ") * F(" << N << ") = "	<< prod2 << std::endl;
		std::cout << "P3 = F(" << N - 2 << ") * F(" << N << ") = "	<< prod3 << std::endl;
		std::cout << "P2 + P3 = " << prod23 << std::endl;

		std::cout << "ERR = P1 - (P2 + P3) = " << err << std::endl;
		
		std::cout << "Let's check this relationship: ERR = 0...";
		assert(err == 0);
		std::cout << "OK." << std::endl;

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;
	}

	void FactorialTest() 
	{
		std::cout << "******************************* FactorialTest() ********************************" << std::endl << std::endl;

		const unsigned int m = 50;
		const unsigned int M = 100;

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;

		BigInt fact;

		for (unsigned int i = 0; i <= M; ++i)
		{
			fact = Factorial(i);
			std::cout << i << "! = " << fact << std::endl;
		}

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;

		for (unsigned int i = m; i <= M; ++i)
		{
			BigInt factn	= Factorial(i);
			BigInt factn1	= Factorial(i - 1);

			// std::cout << i		<< "! = " << factn	<< std::endl;
			// std::cout << i - 1	<< "! = " << factn1	<< std::endl;

			std::cout << "Let's check this relationship: " << i << "! / " << i - 1 << "! = " << i << std::endl;
		//	factn.PrintBinary();
		//	factn1.PrintBinary();
			BigInt ratio = factn / factn1;

			std::cout << i << "! / " << i - 1 << "! = " << ratio << std::endl;
			if (i != 59 && i != 60 && i != 61)
				assert(ratio == i);
			else
				int foo = 5;
		}

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;

		std::cout << "STRESS TEST: Let's calc. 100! / 2!...";

		BigInt f1 = Factorial(100);
		f1 /= Factorial(2);
		std::cout << "OK." << std::endl;
		std::cout << "(" << f1 << ")" << std::endl;

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;

		std::cout << "STESS TEST: Let's calc. (100! + 1) % 20!...";

		BigInt f2 = Factorial(100) + 1;
		f2 %= Factorial(20);
		assert(f2 == 1);
		std::cout << "OK." << std::endl;

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;

		std::cout << "STRESS TEST: Let's calc. 100! % 2!...";

		BigInt f3 = Factorial(100);
		f3 %= Factorial(2);
		assert(f3 == 0);
		std::cout << "OK." << std::endl;

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;
	}

	void ShiftTest()
	{
		std::cout << "********************************* ShiftTest() **********************************" << std::endl << std::endl;

		const unsigned int N = 200;

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;

		BigInt toBeShifted		= 1;
		BigInt toBeMultiplied	= 1;

		for (unsigned int i = 1; i <= N; ++i)
		{
			std::cout << "Left shift check (" << i << ")...";
			toBeShifted		<<= 1;
			toBeMultiplied	*=	2;
			assert(toBeShifted == toBeMultiplied);
			std::cout << "OK." << std::endl;
		}

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;

		BigInt oneShotCheck		= 1;

		oneShotCheck <<= N;

		std::cout << "Left shift verificaion...";
		assert(oneShotCheck == toBeShifted);
		std::cout << "OK." << std::endl;

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;

		for (unsigned int i = 1; i <= N; ++i)
		{
			std::cout << "Right shift check (" << i << ")...";
			toBeShifted		>>= 1;
			toBeMultiplied	/=	2;
			assert(toBeShifted == toBeMultiplied);
			std::cout << "OK." << std::endl;
		}

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;

		oneShotCheck >>= N;

		std::cout << "Right shift verificaion...";
		assert(oneShotCheck == 1);
		std::cout << "OK." << std::endl;

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;
	}

	void SignTest() 
	{
		std::cout << "********************************** SignTest() **********************************" << std::endl << std::endl;

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;

		const unsigned int N = 50;

		std::cout << "b1 = " << N << "!" << std::endl;
		BigInt b1 = Factorial(N);
		std::cout << "b2 = " << N << "!" << std::endl;
		BigInt b2 = Factorial(N);
		std::cout << "Let's check this relationship: Sign(b1) == Sign(b2)...";  
//		assert(b1.CompareSigns(b2));
		std::cout << "OK." << std::endl;

		std::cout << "b2 *= -1" << std::endl;
		b2 *= -1;
		std::cout << "Let's check this relationship: Sign(b1) != Sign(b2)...";  
//		assert(!b1.CompareSigns(b2));
		std::cout << "OK." << std::endl;

		std::cout << "b1 = -b1" << std::endl;
		b1 = -b1;
		std::cout << "Let's check this relationship: Sign(b1) == Sign(b2)...";  
	//	assert(b1.CompareSigns(b2));
		std::cout << "OK." << std::endl;

		std::cout << "b1 /= b1" << std::endl;
		b1 /= b1;
		std::cout << "Let's check this relationship: Sign(b1) == 1...";  
		assert(b1 == 1);
		std::cout << "OK." << std::endl;

		std::cout << "b2 /= b2" << std::endl;
		b2 /= b2;
		std::cout << "Let's check this relationship: Sign(b2) == 1...";  
		assert(b2 == 1);
		std::cout << "OK." << std::endl;

		std::cout << "b1 /= b2" << std::endl;
		b1 /= b2;
		std::cout << "Let's check this relationship: Sign(b1) == 1...";  
		assert(b1 == 1);
		std::cout << "OK." << std::endl;

		std::cout << "--------------------------------------------------------------------------------" << std::endl << std::endl;
	}
}